home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 1.iso
/
ARGONET
/
PD
/
PROGRAMMING
/
DESKLIBC
/
SOURCES.ZIP
/
DeskLib
/
!DLSources
/
Libraries
/
Mem
/
c
/
MidExtend
< prev
next >
Wrap
Text File
|
1995-06-18
|
12KB
|
422 lines
/*
#### # # # #
# # # # # The FreeWare C library for
# # ## ### # # # # ### RISC OS machines
# # # # # # # # # # # ___________________________________
# # #### ### ## # # # #
# # # # # # # # # # Please refer to the accompanying
#### ### #### # # ##### # ### documentation for conditions of use
________________________________________________________________________
File: Mem.MidExtend.c
Author: Copyright © 1993, 1995 Jason Williams and Jason Howat
Version: 2.00 (18 Jun 1995)
Purpose: Dynamic memory manager - reallocation
*/
#include "Desklib:Error.h"
#include "MemDefs.h"
#include <string.h>
#ifdef MEM__DEBUG
#include <stdio.h>
#endif
static BOOL Mem__MidExtendNoCompact(mem_anchor *anchor, mem_header *chunk, int at, int by)
{
int available = chunk->realsize,
olddatasize,
newdatasize;
mem_header *nextchunk = (mem_header *)((int)chunk + chunk->realsize),
*prevchunk = (mem_header *)((int)chunk - chunk->prevrealsize),
*newchunk = chunk;
BOOL now_last = (chunk == mem__lastchunk);
char *source,
*destination;
/* check space in adjoining blocks and use if available */
if((chunk != mem__lastchunk) && (ISFREE(nextchunk)))
{
available += nextchunk->realsize;
now_last = (nextchunk == mem__lastchunk);
}
if(((int)chunk != mem__heap) && (ISFREE(prevchunk)))
{
available += prevchunk->realsize;
newchunk = prevchunk;
}
newdatasize = chunk->datasize + by;
source = (char *)chunk + sizeof(mem_header);
olddatasize = chunk->datasize;
if(available >= CHUNKSIZE(chunk->datasize + by))
{
destination = (char *)((int)newchunk + sizeof(mem_header));
*chunk->handle = destination;
newchunk->handle = chunk->handle;
newchunk->realsize = available;
newchunk->datasize = newdatasize;
mem__free -= WORDALIGN(newdatasize) - WORDALIGN(olddatasize);
if(!now_last)
{
mem_header *nn = (mem_header *)((int)newchunk + available);
nn->prevrealsize = available;
}
memmove(destination, source, at);
memmove(destination + at + by, source + at, olddatasize - at);
Mem__SplitOffFreeChunk(newchunk);
}
else /* try to alloc block of new size and copy old one over */
{
if(!Mem_Alloc((mem_anchor *)&destination, newdatasize))
return FALSE;
memmove(destination, source, at);
memmove(destination + at + by, source + at, olddatasize - at);
Mem_Free(anchor);
*anchor = destination;
Mem__FindChunk((mem_anchor *)&destination, &chunk);
chunk->handle = anchor;
}
return TRUE;
}
extern BOOL Mem_MidExtend(mem_anchor *anchor, int at, int by)
/* Enlarges or reduces a Mem chunk by moving all data beyond 'at' by
* 'by' bytes up or down in memory. 'at' is an offset within the chunk.
*
* Goes to rather a lot of effort to avoid moving the base address of
* this chunk and others, but if mem_autocompact allows, it will compact
* if it is absolutely necessary in order to manage the extension.
*
* Returns TRUE if it succeeds
*/
{
mem_header *chunk,
*bestfit = NULL,
*start, *end,
*best_start = NULL, *best_end,
*scan_start, *scan_end,
*end_of_heap;
int bestfit_size,
best_move, best_free,
move_count, free_count,
needed,
newdatasize, extchunksize;
char *source,
*destination;
if(by == 0) /* Zero bytes? Certainly sir! Anything else? */
return(TRUE);
if(at < 0 || !Mem__FindChunk(anchor, &chunk))
return(FALSE);
if(at > chunk->datasize)
return(FALSE);
source = (char *)chunk + sizeof(mem_header);
/* ------ Reduce chunk ------ */
if (by < 0) /* Reducing the size of the chunk */
{
if (-by > at) by = -at; /* Can't delete past start of chunk */
mem__free += WORDALIGN(chunk->datasize) - WORDALIGN(chunk->datasize + by);
memmove(source + at + by, source + at, chunk->datasize - at);
chunk->datasize += by; /* Shrink data area of this block */
Mem__SplitOffFreeChunk(chunk); /* Return any free space for use */
/* The anchor has not changed */
return(TRUE);
}
/* ------ Extend chunk ------ */
if(mem_autocompact == mem_NOCOMPACT)
return Mem__MidExtendNoCompact(anchor, chunk, at, by);
else /* allowed to relocate other blocks in the search for the cheapest way
* of creating enough space for the extension
*/
{
newdatasize = chunk->datasize + by;
extchunksize = CHUNKSIZE(newdatasize);
/* needed: is the number of free bytes required to perform the extension
(will be a multiple of 4) */
needed = WORDALIGN(newdatasize) - WORDALIGN(chunk->datasize);
/* ensure there is enough space _before_ we go searching for the cheapest
* way of using it
*/
if(mem__free < needed)
{
/* extend the heap by at least (needed - mem__free) bytes */
int extra = needed - mem__free,
prevsize = mem__heapsize;
mem__heapsize = Mem__HeapSize(mem__heapsize + extra + 16);
mem__free += mem__heapsize - prevsize;
#ifdef MEM__DEBUG
fprintf(stderr, "grow heap: %8x\n", mem__heapsize - prevsize);
fflush(stderr);
#endif
mem__lastchunk->realsize = mem__heap + mem__heapsize - (int)mem__lastchunk;
if(mem__free < needed)
{
Mem__SplitOffFreeChunk(mem__lastchunk);
Mem__ReduceSlot();
return FALSE; /* unable to get enough freespace to do extension */
}
}
start = chunk;
end = (mem_header *)((int)chunk + chunk->realsize);
end_of_heap = (mem_header *)(mem__heap + mem__heapsize);
if(start->realsize >= extchunksize)
{
bestfit = start;
bestfit_size = start->realsize;
}
move_count = start->datasize;
free_count = start->realsize - CHUNKSIZE(start->datasize);
/* Scan backwards from chunk to find start of possible run of blocks */
while((free_count < needed) && ((int)start != mem__heap))
{
int used;
start = (mem_header *)((int)start - start->prevrealsize);
if(ISFREE(start))
{
used = 0;
if(((bestfit == NULL) || (bestfit_size > start->realsize)) &&
(start->realsize >= extchunksize))
{
bestfit = start;
bestfit_size = bestfit_size;
}
}
else
{
used = CHUNKSIZE(start->datasize);
move_count += used;
}
free_count += start->realsize - used;
}
scan_start = start;
/* Alternately advance end/start forwards keeping track of count of bytes
to be moved, remembering start/end points for run of blocks with
minimum. At the same time scan for a single free block which is big
enough to hold the entire extended block. Check for when start == chunk
and adjust *_count variables accordingly */
while(1)
{
/* advance end and adjust *_count stats until free_count >= needed */
while((free_count < needed) && (end < end_of_heap))
{
int used;
if(ISFREE(end))
{
used = 0;
if(((bestfit == NULL) || (bestfit_size > end->realsize)) &&
(end->realsize >= extchunksize))
{
bestfit = end;
bestfit_size = bestfit_size;
}
}
else
{
used = CHUNKSIZE(end->datasize);
move_count += used;
}
free_count += end->realsize - used;
end = (mem_header *)((int)end + end->realsize);
}
/* check stats and copy to best_* if needed */
if((free_count >= needed) &&
((best_start == NULL) || (best_move > move_count)))
{
best_start = start;
best_end = end;
best_move = move_count;
best_free = free_count;
}
if(start == chunk)
break;
{
int used;
/* adjust *_count stats and advance start one block */
if(!ISFREE(start))
{
used = CHUNKSIZE(start->datasize);
move_count -= used;
}
else
used = 0;
free_count -= start->realsize - used;
}
start = (mem_header *)((int)start + start->realsize);
if(start == chunk)
move_count -= sizeof(mem_header) + at;
}
scan_end = end;
/* Consider cost of moving whole block to larger free block and choose
* cheapest option for the extension of the block.
* NB: as we have already ensured that (mem__free >= needed), best_start
* must be non-NULL, as a run of blocks has to exist.
*/
if(best_move > chunk->datasize)
{
/* check chunks before scan_start and after scan_end for single free
* block that is large enough for the extended block, as it is cheaper
* to relocate target block.
*/
mem_header *check;
check = (mem_header *)mem__heap;
while(check < scan_start)
{
if(ISFREE(check))
{
if(((bestfit == NULL) || (bestfit_size > check->realsize)) &&
(check->realsize >= extchunksize))
{
bestfit = check;
bestfit_size = check->realsize;
}
}
check = (mem_header *)((int)check + check->realsize);
}
check = scan_end;
while(check < end_of_heap)
{
if(ISFREE(check))
{
if(((bestfit == NULL) || (bestfit_size > check->realsize)) &&
(check->realsize >= extchunksize))
{
bestfit = check;
bestfit_size = check->realsize;
}
}
check = (mem_header *)((int)check + check->realsize);
}
}
if((bestfit != NULL) && (best_move > chunk->datasize))
{
/* it is cheaper to relocate entire chunk */
#ifdef MEM__DEBUG
fprintf(stderr, "relocate: bestfit %p\n", bestfit);
fflush(stderr);
#endif
/* copy data */
destination = (char *)bestfit + sizeof(mem_header);
memmove(destination, source, at);
memmove(destination + at + by, source + at, chunk->datasize - at);
/* update heap control structures */
bestfit->handle = chunk->handle;
bestfit->datasize = newdatasize;
Mem_Free(anchor);
*bestfit->handle = destination;
mem__free -= extchunksize;
Mem__SplitOffFreeChunk(bestfit);
return(TRUE);
}
else
{
/* perform extension by:
* compacting region defined by best_start/best_end
* move data to new locations
* update headers of surrounding and target chunks
*/
mem_header *newchunk;
int newchunksize,
oldchunksize;
if(best_start < chunk) /* must be free space available before chunk */
{
Mem__ShuffleDown(best_start, chunk);
newchunk = (mem_header *)((int)chunk - chunk->prevrealsize);
}
else
newchunk = chunk;
destination = (char *)((int)newchunk + sizeof(mem_header));
if(best_end > (mem_header *)((int)chunk + chunk->realsize))
Mem__ShuffleUp(chunk, best_end);
newchunksize = chunk->realsize;
if(best_start < chunk)
newchunksize += newchunk->realsize;
if(extchunksize > newchunksize) /* sanity check */
Error_ReportFatalInternal(0, "Unexpected failure in Mem_MidExtend");
/* update anchors/links */
oldchunksize = chunk->datasize;
newchunk->handle = chunk->handle;
newchunk->datasize = newdatasize;
newchunk->realsize = newchunksize;
*(newchunk->handle) = destination;
/* move data */
memmove(destination, source, at);
memmove(destination + at + by, source + at, oldchunksize - at);
if(chunk != mem__lastchunk)
{
mem_header *next = (mem_header *)((int)newchunk + newchunksize);
next->prevrealsize = newchunksize;
}
else
mem__lastchunk = newchunk; /* update mem__lastchunk */
Mem__SplitOffFreeChunk(newchunk);
}
}
mem__free -= needed; /* successful allocation - update stats */
return(TRUE);
}